package yartiss.service.viewmodel;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;

import javax.swing.BoundedRangeModel;
import javax.swing.DefaultBoundedRangeModel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

import yartiss.engine.simulation.energy.profiles.consumption.ConsumptionProfileFactory;
import yartiss.engine.simulation.io.ITaskElementVisitor;
import yartiss.engine.simulation.io.XmlFileWriter;
import yartiss.engine.simulation.system.ITask;
import yartiss.engine.simulation.system.SystemMetrics;
import yartiss.engine.simulation.system.schedulable.SchedulableFactory;
import yartiss.engine.simulation.system.taskset.generation.ITaskGenerator;
import yartiss.engine.simulation.system.taskset.generation.ITaskSetGenerator;
import yartiss.engine.simulation.system.taskset.generation.UUniFastDiscardTaskSetGen;
import yartiss.engine.simulation.system.taskset.generation.task.DefaultTaskGenerator;
import yartiss.engine.simulation.system.taskset.generation.task.GraphTaskGenerator;
import yartiss.framework.concurrency.Consumer;
import yartiss.framework.concurrency.IElementConsumedListener;
import yartiss.framework.concurrency.Producer;
import yartiss.framework.concurrency.QueueEntry;
import yartiss.framework.view.AbstractView;
import yartiss.framework.view.IView;
import yartiss.framework.viewmodel.AbstractViewModel;

public class TaskSetsGenViewModel extends AbstractViewModel{

	private boolean isContentChanged = true;
	private boolean isGlobalConsumption = true;
	private boolean variateU=true;
	private int nbProc = 1;
	private int Pr=10;
	private int count=10;
	private int Emax=200;
	private double utilization=0.7, energyUtilization=0.7;
	private int nbTasks=10;
	private String selectedConsumptionProfile=ConsumptionProfileFactory.getAvailableProfiles()[0];
	private String selectedTaskType = getAvailibaleTaskTypes()[1];
	private AbstractView view;
	private final BoundedRangeModel globalProgressModel = new DefaultBoundedRangeModel(0, 0, 0, count);
	private String systemsFile = "file";

	@Override
	public IView getView() {
		return view;
	}

	public void setView(AbstractView view) {
		unregisterViewListeners();
		this.view = view;
		view.setDataContext(this);
		registerViewListeners();		
	}

	private PropertyChangeListener listener = new PropertyChangeListener() {
		@Override	public void propertyChange(PropertyChangeEvent evt) {
			Object value = view.getProperty(evt.getPropertyName());
			setProperty(evt.getPropertyName(), value);
			setIsContentChanged(true);
		}
	};

	private void unregisterViewListeners(){
		if(view != null)
			view.unregisterPropertyChangeListener(listener);
	}

	private void registerViewListeners(){
		if(view != null)
			view.registerPropertyChangeListerner(listener);
	}

	/**
	 * @return the isContentChanged
	 */
	public boolean getIsContentChanged() {
		return isContentChanged;
	}

	public void setIsContentChanged(boolean isContentChanged) {
		this.isContentChanged = isContentChanged;
		firePropertyChanged("IsContentChanged", isContentChanged, isContentChanged);
	}


	public String [] getAvailibaleConsumptionProfiles() {
		return ConsumptionProfileFactory.getAvailableProfiles();
	}

	public String [] getAvailibaleTaskTypes() {
		return SchedulableFactory.availableTypes;
	}

	/**
	 * @return the isGlobalConsumption
	 */
	public boolean getIsGlobalConsumption() {
		return isGlobalConsumption;
	}

	/**
	 * @param isGlobalConsumption the isGlobalConsumption to set
	 */
	public void setIsGlobalConsumption(boolean isGlobalConsumption) {
		this.isGlobalConsumption = isGlobalConsumption;
		firePropertyChanged("IsGlobalConsumption", isGlobalConsumption, isGlobalConsumption);
	}


	/**
	 * @return the selectedPolicyName
	 */
	public String getSelectedConsumptionProfile() {
		return selectedConsumptionProfile;
	}

	/**
	 * @param selectedPolicyName the selectedPolicyName to set
	 */
	public void setSelectedConsumptionProfile(String selectedConsumptionProfile) {
		this.selectedConsumptionProfile = selectedConsumptionProfile;
		firePropertyChanged("SelectedConsumptionProfile", selectedConsumptionProfile, selectedConsumptionProfile);
	}

	/**
	 * @return the selectedPolicyName
	 */
	public String getSelectedTaskType() {
		return selectedTaskType;
	}

	/**
	 * @param selectedPolicyName the selectedPolicyName to set
	 */
	public void setSelectedTaskType(String selectedTaskType) {
		this.selectedTaskType = selectedTaskType;
		firePropertyChanged("SelectedTaskType", selectedTaskType, selectedTaskType);
	}

	/**
	 * @return the nbProc
	 */
	public int getNbProc() {
		return nbProc;

	}

	/**
	 * @param nbProc the nbProc to set
	 */
	public void setNbProc(int nbProc) {
		this.nbProc = nbProc;
		firePropertyChanged("NbProc", nbProc, nbProc);
	}

	/**
	 * @return the emax
	 */
	public int getEmax() {
		return Emax;
	}

	/**
	 * @param emax the emax to set
	 */
	public void setEmax(int emax) {
		Emax = emax;
		firePropertyChanged("Emax", emax, emax);
	}

	/**
	 * @return the pr
	 */
	public int getPr() {
		return Pr;
	}

	/**
	 * @param pr the pr to set
	 */
	public void setPr(int pr) {
		Pr = pr;
		firePropertyChanged("Pr", pr, pr);
	}

	/**
	 * @return the count
	 */
	public int getCount() {
		return count;
	}

	/**
	 * @param count the count to set
	 */
	public void setCount(int count) {
		this.count = count;
		globalProgressModel.setMaximum(count);
		firePropertyChanged("Count", count, count);
	}

	/**
	 * @return the nbTasks
	 */
	public int getNbTasks() {
		return nbTasks;
	}

	/**
	 * @param nbTasks the nbTasks to set
	 */
	public void setNbTasks(int nbTasks) {
		this.nbTasks = nbTasks;
		firePropertyChanged("NbTasks", nbTasks, nbTasks);
	}

	/**
	 * @return the utilization
	 */
	public double getUtilization() {
		return utilization;
	}

	/**
	 * @param utilization the utilization to set
	 */
	public void setUtilization(double utilization) {
		this.utilization = utilization;
		firePropertyChanged("Utilization", utilization, utilization);
	}

	/**
	 * @return the energyUtilization
	 */
	public double getEnergyUtilization() {
		return energyUtilization;
	}

	/**
	 * @param energyUtilization the energyUtilization to set
	 */
	public void setEnergyUtilization(double energyUtilization) {
		this.energyUtilization = energyUtilization;
		firePropertyChanged("EnergyUtilization", energyUtilization, energyUtilization);
	}

	/**
	 * @return the variateU
	 */
	public boolean getVariateU() {
		return variateU;
	}

	/**
	 * @param variateU the variateU to set
	 */
	public void setVariateU(boolean variateU) {
		this.variateU = variateU;
	}


	/**
	 * @return the systemsFile
	 */
	public String getSystemsFile() {
		return systemsFile;
	}

	/**
	 * @param systemsFile the systemsFile to set
	 */
	public void setSystemsFile(String systemsFile) {
		this.systemsFile = systemsFile;
		firePropertyChanged("SystemsFile", systemsFile, systemsFile);
	}


	/**
	 * @return the globalProgressModel
	 */
	public BoundedRangeModel getGlobalProgressModel() {
		return globalProgressModel;
	}

	/**
	 * @return the generateCommand
	 */
	public ActionListener getGenerateCommand() {
		return generateCommand;
	}

	private final ActionListener generateCommand = new ActionListener() {
		@Override public void actionPerformed(ActionEvent e) {
			new Thread(new Runnable() {
				public void run() {
					SwingUtilities.invokeLater(new Runnable() {
						public void run() {
							SwingWorker<Void, String> task = new SwingWorker<Void, String>() {
								
								private int counter = 0;
								private boolean finished;
								@Override	protected Void doInBackground() throws Exception {
									finished = false;
									boolean energy;
									double padUnit = 0.05, pad = 0;
									long n, nb;
									do {
										pad += padUnit;
										n = Math.round(1 / pad);
										nb = count / (n * n);
									} while (nb <= 0);
									if (pad >= 1) {
										pad = 0.9;
										n = 1;
										nb = count;
									}
									counter = 1;
									final ITaskElementVisitor writer = new XmlFileWriter(systemsFile, count, nbTasks);
									BlockingQueue<QueueEntry<ITask[]>> queue = new LinkedBlockingQueue<QueueEntry<ITask[]>>(100000);

									final Consumer<ITask[]> consumer = new Consumer<ITask[]>(queue,
											new IElementConsumedListener<ITask[]>() {
												@Override
												public void consume(ITask[] tasks) {
													try {
														if (tasks.length < 1 || counter > count) {
															System.out.println("hehe");
															return;
														}

														double u = Math.round(SystemMetrics.utilisation(Arrays.asList(tasks)) * 1000) / 1000.0d;
														double ue = Math.round(SystemMetrics.energyUtilization(Arrays.asList(tasks), Pr) * 1000) / 1000.0d;

														System.out.println(counter + "\t: U=" + u + "\tUe="+ ue);
														writer.visitTaskset(tasks, Pr);
														if (counter >= count) {
															finished = true;
															System.out.println("writer closed!");
															writer.close();
														}
														counter++;
														globalProgressModel.setValue(counter);

													} catch (IOException e) {
														e.printStackTrace();
													}
												}
											});
									consumer.start();
									do {
										final Producer<ITask[]> producer = new Producer<ITask[]>(queue);
										for (int i = 1; i <= n; i++) {
											for (int j = 1; j <= n; j++) {
												if (i * pad < 1 && j * pad < 1) {
													ITaskGenerator generator;
													if (selectedTaskType.toLowerCase().equals("simple")){
														generator = new DefaultTaskGenerator();
														energy = true;
													}
													else if (selectedTaskType.toLowerCase().equals("graph")){
														System.out.println("prodiucer:; graph");
														generator = new GraphTaskGenerator();
														energy = false;
													}
													else
														throw new IllegalArgumentException("Task model ("+ selectedTaskType + ") doesn't exist...");

													final ITaskSetGenerator taskSetGenerator = new UUniFastDiscardTaskSetGen(generator, nbTasks, nbProc, i * pad, j * pad, Pr, Emax, 0, false, energy);
													for (int k = 0; k < nb; k++) {
														producer.produce(new Callable<ITask[]>() {
															@Override
															public ITask[] call() throws Exception {
																if (finished) {
																	return null;
																}

																ITask[] tasks = taskSetGenerator.generate();
																if (tasks == null){
																	//System.out.println("NULL");
																	return new ITask[] {};
																}
//																for (ITask iTask : tasks) {
//																	System.out.println("ITASK: "+iTask.toString());
//																}
																return tasks;
															}
														});
													}
												}
											}
										}
										Thread.sleep(1000);
										producer.awaitTermination();
									} while (counter < count);
									queue.put(new QueueEntry<ITask[]>(null));
									System.out.println("fini");
									return null;
								}
							};
							task.execute();
						}
					});
				}
			}).start();
		}
	};
}
